package customer

import (
	"context"
	"database/sql"
	stdErrors "errors"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize"
	"github.com/google/wire"
	"reflect"
	"sieve_admin_server_userpass/app/dao"
	"sieve_admin_server_userpass/app/dao/recharge"
	"sieve_admin_server_userpass/app/dao/user"
	"sieve_admin_server_userpass/app/schema"
	schemaCustomer "sieve_admin_server_userpass/app/schema/customer"
	serviceLog "sieve_admin_server_userpass/app/service/logger"
	"sieve_admin_server_userpass/app/service/sys"
	serviceUtil "sieve_admin_server_userpass/app/service/util"
	"sieve_admin_server_userpass/lib/log"
	"sieve_admin_server_userpass/lib/mysql"
	"sieve_admin_server_userpass/lib/util/convert"
	"sieve_admin_server_userpass/lib/util/errors"
	pkgTime "sieve_admin_server_userpass/pkg/time"
	pkgUtil "sieve_admin_server_userpass/pkg/util"
	"strings"
	"time"
)

var (
	RechargeSet = wire.NewSet(wire.Struct(new(RechargeSrv), "*"))
)

type RechargeSrv struct {
	Trans            *dao.Trans
	UserSrv          *UserSrv
	RoleSrv          *sys.RoleSrv
	BusinessSrv      *serviceLog.BusinessSrv
	DailyRechargeSrv *DailyRechargeSrv
	UserRepo         *dao.UserRepo
	RechargeRepo     *dao.RechargeRepo
}

func (a *RechargeSrv) requestCheck(item map[string]interface{}) error {

	if _, ok := item["type"]; !ok {
		return stdErrors.New("请求参数错误")
	}

	ty := item["type"].(uint64)
	if ty == 1 {

		var rpZero, bpZero bool

		if v, ok := item["recharge_points"]; !ok {
			return stdErrors.New("请填写充值积分")
		} else {
			if v.(uint64) < 0 {
				return stdErrors.New("请输入正确的数字（数据范围(0-9999999)）")
			}
			if v.(uint64) >= 99999990000 {
				return stdErrors.New("数字太大，暂不支持")
			}

			rpZero = v.(uint64) == 0
		}

		if v, ok := item["bonus_points"]; ok {
			if v.(uint64) < 0 {
				return stdErrors.New("请输入正确的数字（数据范围(0-9999999)）")
			}
			if v.(uint64) >= 99999990000 {
				return stdErrors.New("数字太大，暂不支持")
			}

			bpZero = v.(uint64) == 0
		}

		if rpZero && bpZero {
			return stdErrors.New("请填写'充值积分'或者'赠送积分'")
		}
	}

	if ty == 2 {
		if v, ok := item["refund_points"]; ok {
			if v.(uint64) <= 0 {
				return stdErrors.New("请输入正确的数字（数据范围(0-9999999)）")
			}
			if v.(uint64) >= 99999990000 {
				return stdErrors.New("数字太大，暂不支持")
			}
		}
	}

	return nil
}

func (a *RechargeSrv) Create(ctx context.Context, uid uint64, item map[string]interface{}) interface{} {

	if err := a.requestCheck(item); err != nil {
		return err
	}

	var (
		now = time.Now().Unix()
		def = map[string]interface{}{
			"creator":    uid,
			"create_at":  now,
			"updated_at": now,
		}
		totalPointsValue   uint64
		willRefundPoints   uint64
		willRp             uint64
		willBp             uint64
		noCheckAccountSelf bool // 是否不需要检测自己的账户余额是否够本次充值
		ty                 = item["type"].(uint64)
		refundTy           = uint64(1)
	)

	if v, ok := item["refund_ty"]; ok {
		refundTy = v.(uint64)
	}
	delete(item, "refund_ty")

	for k, v := range def {
		item[k] = v
	}

	var (
		logType = "业务日志"
		logName = "充值"
		logMsg  string
	)

	defer func() {
		go func() {
			a.BusinessSrv.CreateBusinessRecord(ctx, uid, logType, logName, logMsg)
			t := time.Now()
			// 只统计充值
			if ty == 1 {
				a.DailyRechargeSrv.UpdateRechargeStatistics(time.Date(t.Year(), t.Month(), t.Day()+1, 0, 0, 0, 0, time.Local), item["account_id"].(uint64))
				_ = a.UserSrv.UpdateLastRecharge(ctx, item["account_id"].(uint64), t.Unix())
			}
		}()
	}()

	// 获取充值人的账户余额
	var respCreator *user.User
	if _respCreator, err := a.UserSrv.GetByUID(ctx, uid); err != nil {
		return err
	} else {
		respCreator = _respCreator
	}

	// 渠道主没有退款权限
	if ty == 2 && a.RoleSrv.IsChannelAgentRole(respCreator.RoleId) {
		return stdErrors.New("当前角色没有退款权限")
	}

	opts := &sql.TxOptions{
		Isolation: sql.LevelReadCommitted,
	}
	err := a.Trans.TransactionTx(ctx, func(ctx context.Context, t *mysql.Tx) error {

		// tp, _, _, err := a.getLatestTotalPoints(ctx, item["account_id"].(uint64), true)
		// if err != nil {
		//	log.Err(fmt.Sprintf("get latest total points err:%+v", err))
		//	return err
		// }

		var tp uint64
		_respUser, err := a.UserSrv.GetByUID(ctx, item["account_id"].(uint64), true)
		if err != nil {
			log.Err(fmt.Sprintf("get latest total points err:%+v", err))
			return err
		} else {
			tp = _respUser.RechargePoints + _respUser.BonusPoints
		}

		var (
			userBonusPoints uint64
			clearPoints     bool
			extra           = make(map[string]interface{})
		)
		// ty == 1 表示充值
		if ty == 1 {

			if f, ok := item["recharge_points"]; ok {
				totalPointsValue += f.(uint64)
				willRp = f.(uint64)
			}

			if f, ok := item["bonus_points"]; ok {
				totalPointsValue += f.(uint64)
				willBp = f.(uint64)
			}

			/*
				1. 需要判断，充值人的账户余额是否足以支付被充值人的充值积分
				1.1 被充值人的 充值积分和 赠送积分都从充值人处扣除（先扣除赠送积分，再扣除普通积分）
				1.2 被充值人的 赠送积分也是从 充值人处扣除
				1.3 管理员,业务员给代理商充值是没有限额的			--- 此处的逻辑是 管理员,业务员充值是没有限额的,不管给谁充值
			*/

			/*
					2. 代理商给渠道主充值的积分走向是按照 "先扣除赠送积分，再扣除赠送积分" 来处理的

				例如：代理商 agent_001 的账号有100充值积分，50赠送积分，渠道主 user_01 有赠送积分:0,充值积分:0
					1.1 当 代理商 agent_001 给 渠道主 user_01 充值 赠送积分:60后,
						代理商 agent_001 的账号有赠送积分:90,充值积分:0，渠道主 user_01 有赠送积分:60,充值积分:0
					1.2 当 代理商 agent_001 再次给 渠道主 user_01 充值 充值积分:60 后,
						代理商 agent_001 的账号有赠送积分:30,充值积分:0，渠道主 user_01 有赠送积分:60,充值积分:60

				3. 代理商给渠道主扣款的积分走向是按照 "扣除赠送积分，归还赠送积分，扣除充值积分，归还充值积分" 来处理的

				例如：代理商 agent_001 的账号有赠送积分:0,充值积分:0，渠道主 user_01 有赠送积分:100,充值积分:100
					1.1 当 代理商 agent_001 给 渠道主 user_01 扣款 赠送积分:60后,
						代理商 agent_001 的账号有赠送积分:60,充值积分:0，渠道主 user_01 有赠送积分:40,充值积分:100
					1.2 当 代理商 agent_001 再次给 渠道主 user_01 扣款 充值积分:60 后,
						代理商 agent_001 的账号有赠送积分:60,充值积分:60，渠道主 user_01 有赠送积分:40,充值积分:40
			*/

			// 管理员给代理商充值
			// 代理商给渠道主充值
			if !((a.RoleSrv.IsAdminRoleId(respCreator.RoleId) && a.RoleSrv.IsAgentRole(_respUser.RoleId)) ||
				(a.RoleSrv.IsSalespersonRole(respCreator.RoleId) && a.RoleSrv.IsAgentRole(_respUser.RoleId)) ||
				(a.RoleSrv.IsAgentRole(respCreator.RoleId) && a.RoleSrv.IsChannelAgentRole(_respUser.RoleId))) {
				return stdErrors.New("当前角色没有给充值对象充值权限")
			}

			if a.RoleSrv.IsAdminRoleId(respCreator.RoleId) || a.RoleSrv.IsSalespersonRole(respCreator.RoleId) {
				noCheckAccountSelf = true
			}

			if !noCheckAccountSelf {

				if _respCreator, err := a.UserSrv.GetByUID(ctx, uid, true); err != nil {
					return err
				} else {
					respCreator = _respCreator
				}

				// if respCreator.RechargePoints+respCreator.BonusPoints < totalPointsValue {
				//	logType = "异常日志"
				//	logMsg = "账户余额不足以支付本次充值"
				//	return errors.New(logMsg)
				// }

				if respCreator.RechargePoints < willRp || respCreator.BonusPoints < willBp {
					logType = "异常日志"
					logMsg = "账户余额不足以支付本次充值"
					return errors.New(logMsg)
				}

				/*
					1. account_tp 表示充值人的账户总额
					2. account_recharge_points,account_bonus_points 表示给充值人的账户金额(普通积分和赠送积分)
					3. will_recharge_points,will_recharge_points 表示给被充值人的充值金额
				*/

				extra["id"] = respCreator.ID
				extra["will_tp"] = totalPointsValue
				extra["will_recharge_points"] = willRp
				extra["will_bonus_points"] = willBp

				extra["account_tp"] = respCreator.RechargePoints + respCreator.BonusPoints
				extra["account_recharge_points"] = respCreator.RechargePoints
				extra["account_bonus_points"] = respCreator.BonusPoints
			}

		}

		// ty == 2 表示退款
		if ty == 2 {

			// 获取被退款人的账户余额
			_respAccount, err := a.UserSrv.GetByUID(ctx, item["account_id"].(uint64), true)
			if err != nil {
				log.Err(fmt.Sprintf("get user info by uid err:%+v", err))
				return err
			}

			if f, ok := item["refund_points"]; ok {
				willRefundPoints = f.(uint64)
			}

			userBonusPoints = _respAccount.BonusPoints
			if refundTy == 1 {
				if _respAccount.RechargePoints < willRefundPoints {
					logType = "异常日志"
					logMsg = "账户充值积分余额不足以支持本次退款"
					return stdErrors.New(logMsg)
				} else {
					willRp = willRefundPoints
				}

				// // 当账户余额被扣款扣完的时候，赠送余额自动清0
				// if _respAccount.RechargePoints == willRefundPoints {
				//	// 加上 clearPoints = true 防止用户账户赠送余额为0的情况
				//	clearPoints = true
				//	userBonusPoints = 0
				// }
			}
			if refundTy == 2 {
				if _respAccount.BonusPoints < willRefundPoints {
					logType = "异常日志"
					logMsg = "账户赠送积分余额不足以支持本次退款"
					return stdErrors.New(logMsg)
				} else {
					willBp = willRefundPoints
				}
			}
		}

		// 剩余积分 = 上一次的总积分
		item["residual_points"] = tp
		if ty == 1 {
			totalPointsValue += tp
		}
		if ty == 2 {
			totalPointsValue = tp - willRefundPoints
		}

		item["total_points"] = totalPointsValue

		// 当账户余额被扣款扣完的时候，赠送余额自动清0,充值记录总额也清0
		if ty == 2 && userBonusPoints == 0 && clearPoints {
			item["total_points"] = userBonusPoints
		}

		if ty == 1 && !noCheckAccountSelf {
			if err := a.UserSrv.DecreaseUserPoints(ctx, extra["id"].(uint64), willBp, willRp); err != nil {
				return err
			}
		}

		if ty == 2 {
			if a.RoleSrv.IsChannelAgentRole(_respUser.RoleId) {
				if err := a.UserSrv.IncreaseUserPoints(ctx, uint64(_respUser.LeaderId), willBp, willRp); err != nil {
					return err
				}
			}
		}

		if err := a.updateUserRechargeInfo(ctx, item, int(ty), int(refundTy), userBonusPoints); err != nil {
			return err
		}

		if err := a.createRetry(ctx, item, 5); err != nil {
			return err
		}
		return nil
	}, opts)

	if err != nil {
		logType = "异常日志"
	}

	return err
}

//
// func (a *RechargeSrv) Create(ctx context.Context, uid uint64, item map[string]interface{}) interface{} {
//
//	if err := a.requestCheck(item); err != nil {
//		return err
//	}
//
//	var (
//		now = time.Now().Unix()
//		def = map[string]interface{}{
//			"creator":    uid,
//			"create_at":  now,
//			"updated_at": now,
//		}
//		totalPointsValue   uint64
//		willRp             uint64
//		willBp             uint64
//		noCheckAccountSelf bool // 是否不需要检测自己的账户余额是否够本次充值
//		ty                 = item["type"].(uint64)
//	)
//
//	for k, v := range def {
//		item[k] = v
//	}
//
//	var (
//		logType = "业务日志"
//		logName = "充值"
//		logMsg  string
//	)
//
//	defer func() {
//		go func() {
//			a.BusinessSrv.CreateBusinessRecord(ctx, uid, logType, logName, logMsg)
//			t := time.Now()
//			// 只统计充值
//			if ty == 1 {
//				a.DailyRechargeSrv.UpdateRechargeStatistics(time.Date(t.Year(), t.Month(), t.Day()+1, 0, 0, 0, 0, time.Local), item["account_id"].(uint64))
//			}
//		}()
//	}()
//
//	// 获取充值人的账户余额
//	var respCreator *user.User
//	if _respCreator, err := a.UserSrv.GetByUID(ctx, uid); err != nil {
//		return err
//	} else {
//		respCreator = _respCreator
//	}
//
//	// 渠道主没有退款权限
//	if ty == 2 && a.RoleSrv.IsChannelAgentRole(respCreator.RoleId) {
//		return stdErrors.New("当前角色没有退款权限")
//	}
//
//	opts := &sql.TxOptions{
//		Isolation: sql.LevelReadCommitted,
//	}
//	err := a.Trans.TransactionTx(ctx, func(ctx context.Context, t *mysql.Tx) error {
//
//		tp, _, _, err := a.getLatestTotalPoints(ctx, item["account_id"].(uint64), true)
//		if err != nil {
//			log.Err(fmt.Sprintf("get latest total points err:%+v", err))
//			return err
//		}
//
//		var (
//			userBonusPoints uint64
//			extra           = make(map[string]interface{})
//		)
//		// ty == 1 表示充值
//		if ty == 1 {
//
//			if f, ok := item["recharge_points"]; ok {
//				totalPointsValue += f.(uint64)
//				willRp = f.(uint64)
//			}
//
//			if f, ok := item["bonus_points"]; ok {
//				totalPointsValue += f.(uint64)
//				willBp = f.(uint64)
//			}
//
//			/*
//				1. 需要判断，充值人的账户余额是否足以支付被充值人的充值积分
//				1.1 被充值人的 充值积分和 赠送积分都从充值人处扣除（先扣除赠送积分，再扣除普通积分）
//				1.2 被充值人的 赠送积分也是从 充值人处扣除
//				1.3 管理员,业务员给代理商充值是没有限额的			--- 此处的逻辑是 管理员,业务员充值是没有限额的,不管给谁充值
//			*/
//
//			if a.RoleSrv.IsAdminRoleId(respCreator.RoleId) || a.RoleSrv.IsSalespersonRole(respCreator.RoleId) {
//				noCheckAccountSelf = true
//			}
//
//			if !noCheckAccountSelf {
//
//				if _respCreator, err := a.UserSrv.GetByUID(ctx, uid, true); err != nil {
//					return err
//				} else {
//					respCreator = _respCreator
//				}
//
//				if respCreator.RechargePoints+respCreator.BonusPoints < totalPointsValue {
//					logType = "异常日志"
//					logMsg = "账户余额不足以支付本次充值"
//					return stdErrors.New(logMsg)
//				}
//
//				/*
//					1. account_tp 表示充值人的账户总额
//					2. account_recharge_points,account_bonus_points 表示给充值人的账户金额(普通积分和赠送积分)
//					3. will_recharge_points,will_recharge_points 表示给被充值人的充值金额
//				*/
//
//				extra["id"] = respCreator.ID
//				extra["will_tp"] = totalPointsValue
//				extra["will_recharge_points"] = willRp
//				extra["will_bonus_points"] = willBp
//
//				extra["account_tp"] = respCreator.RechargePoints + respCreator.BonusPoints
//				extra["account_recharge_points"] = respCreator.RechargePoints
//				extra["account_bonus_points"] = respCreator.BonusPoints
//			}
//
//		}
//
//		// ty == 2 表示退款
//		if ty == 2 {
//
//			// 获取被退款人的账户余额
//			_respAccount, err := a.UserSrv.GetByUID(ctx, item["account_id"].(uint64), true)
//			if err != nil {
//				log.Err(fmt.Sprintf("get user info by uid err:%+v", err))
//				return err
//			}
//
//			if f, ok := item["refund_points"]; ok {
//				totalPointsValue = uint64(0) - f.(uint64)
//			}
//
//			userBonusPoints = _respAccount.BonusPoints
//			if _respAccount.RechargePoints+totalPointsValue < 0 {
//				logType = "异常日志"
//				logMsg = "账户余额不足以支持本次退款"
//				return stdErrors.New(logMsg)
//			}
//
//			// 当账户余额被扣款扣完的时候，赠送余额自动清0
//			if _respAccount.RechargePoints+totalPointsValue == 0 {
//				userBonusPoints = 0
//			}
//		}
//
//		// 剩余积分 = 上一次的总积分
//		item["residual_points"] = tp
//		totalPointsValue += tp
//		item["total_points"] = totalPointsValue
//
//		// 当账户余额被扣款扣完的时候，赠送余额自动清0,充值记录总额也清0
//		if ty == 2 && userBonusPoints == 0 {
//			item["total_points"] = userBonusPoints
//		}
//
//		if ty == 1 && !noCheckAccountSelf {
//			if err := a.UserSrv.DecreaseUserPoints(ctx, extra); err != nil {
//				return err
//			}
//		}
//
//		if err := a.updateUserRechargeInfo(ctx, item, int(ty), userBonusPoints); err != nil {
//			return err
//		}
//
//		if err := a.createRetry(ctx, item, 5); err != nil {
//			return err
//		}
//		return nil
//	}, opts)
//
//	if err != nil {
//		logType = "异常日志"
//	}
//
//	return err
// }

func (a *RechargeSrv) updateUserRechargeInfo(ctx context.Context, item map[string]interface{}, ty, refundTy int, args ...interface{}) error {

	var (
		now       = time.Now().Unix()
		updateSQL string
	)
	if ty == 1 {

		var (
			rp uint64
			bp uint64
		)
		if v, ok := item["recharge_points"]; ok {
			rp = v.(uint64)
		}

		if v, ok := item["bonus_points"]; ok {
			bp = v.(uint64)
		}

		updateSQL = fmt.Sprintf(`
		UPDATE 
		    user 
		SET 
		    updated_at = %v,
		    recharge_points = recharge_points + %v,	
		    bonus_points = bonus_points + %v
		WHERE
		    id = %v
`, now, rp, bp, item["account_id"])
	}

	if ty == 2 {
		if refundTy == 1 {
			var userBonusPoints uint64
			if len(args) == 1 {
				userBonusPoints = args[0].(uint64)
			}
			updateSQL = fmt.Sprintf(`
		UPDATE 
		    user 
		SET 
		    updated_at = %v,
		    recharge_points = recharge_points - %v,
		    bonus_points = %v
		WHERE
		    id = %v
`, now, item["refund_points"], userBonusPoints, item["account_id"])
		}

		if refundTy == 2 {
			updateSQL = fmt.Sprintf(`
		UPDATE 
		    user 
		SET 
		    updated_at = %v,
		    bonus_points = bonus_points - %v
		WHERE
		    id = %v
`, now, item["refund_points"], item["account_id"])
		}
	}

	_, err := a.UserRepo.ExecRaw(ctx, updateSQL)
	return err
}

func (a *RechargeSrv) getLatestTotalPoints(ctx context.Context, accountId uint64, args ...interface{}) (uint64, uint64, uint64, error) {

	var (
		reqArgs = map[string]interface{}{
			"account_id": accountId,
		}
		repKeys = []string{"recharge_points", "bonus_points", "total_points"}
		p       = schema.PaginationParam{
			OnlyData: true,
		}
		tp, bp, rp uint64
		cr         bool
	)

	orderBySQL := fmt.Sprintf("id DESC LIMIT 1")

	var req interface{}
	if len(args) == 1 {

		// 表示是否是当前读，用 FOR UPDATE 语句指定
		if args[0].(bool) {
			cr = true

			orderBySQL += " FOR UPDATE"
		}
	}
	ctx = context.WithValue(ctx, "order_by", orderBySQL)

	if cr {
		var (
			retryCount    = 5
			retryInterval = 100 * time.Millisecond
		)

		// 获取被充值人的账户余额
		for i := 0; i < retryCount; i++ {
			_req, err := a.RechargeRepo.Query(ctx, reqArgs, p, repKeys)
			if err == nil {
				req = _req
				break
			}

			if isLockError(err) || isDeadlineExceededError(err) {
				time.Sleep(retryInterval)
				continue
			}

			return tp, bp, rp, err
		}
	} else {
		_req, err := a.RechargeRepo.Query(ctx, reqArgs, p, repKeys)
		if err != nil {
			return tp, bp, rp, err
		}
		req = _req
	}

	for _, v := range req.(map[string]interface{})["list"].([]interface{}) {

		tp = v.(*recharge.Recharge).TotalPoints
		bp = v.(*recharge.Recharge).BonusPoints
		rp = v.(*recharge.Recharge).RechargePoints

		break
	}

	return tp, bp, rp, nil
}

func (a *RechargeSrv) createRetry(ctx context.Context, items map[string]interface{}, args ...interface{}) error {

	var (
		err           error
		retryCount    = 1
		retryInterval = 100 * time.Millisecond
	)

	if len(args) == 1 {
		retryCount = args[0].(int)
	}
	if len(args) == 2 {
		retryCount, retryInterval = args[0].(int), args[1].(time.Duration)
	}

	for i := 0; i < retryCount; i++ {
		err = a.RechargeRepo.Create(ctx, items)
		if err == nil {
			break
		}
		if isLockError(err) || isDeadlineExceededError(err) {
			time.Sleep(retryInterval)
			continue
		}

		return err
	}

	return err
}

func (a *RechargeSrv) addQueryResponseSeq(p schema.PaginationParam, req interface{}) interface{} {

	if req != nil {

		var enable bool
		if _, ok := req.(map[string]interface{})["total"]; ok {
			if _, ok := req.(map[string]interface{})["list"]; ok {
				enable = true
			}
		}
		if enable {
			var (
				indexStart = 1
				total      = req.(map[string]interface{})["total"].(uint64)
			)

			for i, _ := range req.(map[string]interface{})["list"].([]interface{}) {

				// resp := v.(*schemaCustomer.RechargeQueryRsp)
				seqNo := int(total) - (p.PageSize*(p.PageIndex-1) + (int(i) + 1)) + indexStart
				// seqNo := (p.PageSize*(p.PageIndex-1) + (int(i) + 1)) + autoIndexStart
				// req.(map[string]interface{})["list"].([]interface{})[i].(*schemaCustomer.UserQueryRsp).Seq = int64(seqNo)
				req.(map[string]interface{})["list"].([]interface{})[i].(*schemaCustomer.RechargeQueryRsp).SeqNo = int64(seqNo)
			}
		}
	}

	return req
}

func (a *RechargeSrv) Query(ctx context.Context, uid uint64, reqArgs map[string]interface{}, p schema.PaginationParam) interface{} {

	var (
		rechargeTableName = recharge.Recharge{}.TableName()
		userTableName     = user.User{}.TableName()
		joinString        = fmt.Sprintf(
			`LEFT JOIN %v creator_user ON creator_user.id = %v.creator AND creator_user.deleted = 0
					LEFT JOIN %v account_user ON account_user.id = %v.account_id AND account_user.deleted = 0
					LEFT JOIN (%v) leader_user ON leader_user.id = %v.account_id`,
			userTableName,
			rechargeTableName,
			userTableName,
			rechargeTableName,
			a.UserSrv.GetUserLeaderSQL(),
			rechargeTableName,
		)
		repKeys = []string{
			fmt.Sprintf("%v.id as id", rechargeTableName),
			fmt.Sprintf("%v.account_id as account_id", rechargeTableName),
			fmt.Sprintf("%v.account as account_name", "account_user"),
			fmt.Sprintf("%v.leader_id as leader_id", "account_user"),
			fmt.Sprintf("%v.leader_account as leader_account", "leader_user"),
			fmt.Sprintf("%v.recharge_points as recharge_points", rechargeTableName),
			fmt.Sprintf("%v.bonus_points as bonus_points", rechargeTableName),
			fmt.Sprintf("%v.refund_points as refund_points", rechargeTableName),
			fmt.Sprintf("%v.total_points as total_points", rechargeTableName),
			fmt.Sprintf("%v.residual_points as residual_points", rechargeTableName),
			fmt.Sprintf("%v.creator as creator", rechargeTableName),
			fmt.Sprintf("%v.account as creator_name", "creator_user"),
			fmt.Sprintf("%v.create_at as create_at", rechargeTableName),
		}
	)

	ctx = context.WithValue(
		context.WithValue(ctx, "join", joinString),
		"column_types", schemaCustomer.RechargeQueryRsp{},
	)

	roleId, err := a.UserSrv.GetRoleID(ctx, uid)
	if err != nil {
		return err
	}

	if a.RoleSrv.IsSalespersonRole(roleId) {
		reqArgs["creator"] = uid
	}

	req, err := a.RechargeRepo.Query(ctx, reqArgs, p, repKeys)
	if err != nil {
		return err
	}

	if req != nil {

		var (
			residualPoints uint64 // 剩余积分
			rechargePoints uint64 // 充值积分
			bonusPoints    uint64 // 赠送积分
			refundPoints   uint64 // 退款积分
			totalPoints    uint64 //  积分总额
		)
		for _, v := range req.(map[string]interface{})["list"].([]interface{}) {

			respDRs := v.(*schemaCustomer.RechargeQueryRsp)
			residualPoints += respDRs.ResidualPoints
			rechargePoints += respDRs.RechargePoints
			bonusPoints += respDRs.BonusPoints
			refundPoints += respDRs.RefundPoints
			totalPoints += respDRs.TotalPoints
		}

		req.(map[string]interface{})["all"] = map[string]interface{}{
			"residual_points": residualPoints,
			"recharge_points": rechargePoints,
			"bonus_points":    bonusPoints,
			"refund_points":   refundPoints,
			"total_points":    totalPoints,
		}
	}

	return a.addQueryResponseSeq(p, req)
}

func (a *RechargeSrv) exportRequestCheck(ctx context.Context, reqArgs map[string]interface{}) error {

	if _, ok := reqArgs["ids"]; !ok {
		return errors.New("请求参数缺失")
	}

	if _, ok := reqArgs["seqs"]; !ok {
		return errors.New("请求参数缺失")
	}
	return nil
}

func (a *RechargeSrv) Export(ctx context.Context, uid uint64, item map[string]interface{}, fCallBack func(fileName string)) interface{} {

	if err := a.exportRequestCheck(ctx, item); err != nil {
		return err
	}

	var (
		p = schema.PaginationParam{
			OnlyData:         true,
			OnlyDataButTotal: true,
		}
		queryArgs = map[string]interface{}{
			"id": func() string {
				ids := item["ids"].(string)
				return fmt.Sprintf("(%v)", strings.Join(strings.Split(ids, "|"), ","))
			}(),
		}
		seqs = make(map[string]string)
	)

	for i, v := range strings.Split(item["ids"].(string), "|") {
		seqs[v] = strings.Split(item["seqs"].(string), "|")[i]
	}

	ctx = context.WithValue(ctx,
		"condition", map[string]string{"id": "IN"},
	)

	req := a.Query(ctx, uid, queryArgs, p)
	if req != nil {
		if err, ok := req.(error); ok {
			return err
		}
	}

	selectedItems := []string{
		"id",
		"account_name",
		"residual_points",
		"recharge_points",
		"bonus_points",
		"refund_points",
		"total_points",
		"create_at",
		"creator_name",
	}
	req = a.export(ctx, req, 1, seqs, strings.Join(selectedItems, "|"))
	if req != nil {
		if _, ok := req.(error); ok {
			return req
		}
	}

	fCallBack(req.(string))

	return req
}

func (a *RechargeSrv) export(ctx context.Context, req interface{}, funcTy int, seqs map[string]string, selectedItems string) interface{} {

	var (
		xlsx       = excelize.NewFile()
		categories = map[string]string{
			"A1": "序号",
			"B1": "充值账户",
			"C1": "剩余积分",
			"D1": "充值积分",
			"E1": "赠送积分",
			"F1": "退款积分",
			"G1": "积分总额",
			"H1": "充值时间",
			"I1": "充值人员",
		}
		selectedItemMap = make(map[string]int)
		colWidths       = map[string]float64{"A": 8, "B": 22, "C": 18, "D": 8, "E": 8, "F": 8, "G": 20, "H": 20, "I": 12}
	)

	for key, value := range categories {
		xlsx.SetCellValue("Sheet1", key, value)
		_style, err := xlsx.NewStyle(`{"fill":{"type":"pattern","color":["#EEECE1"],"pattern":1},"alignment":{"horizontal":"center","vertical":"center"},"font":{"bold":true,"family":"宋体","size":10}}`)
		if err == nil {
			xlsx.SetCellStyle("Sheet1", key, key, _style)
		}
	}

	xlsx.SetRowHeight("Sheet1", 1, 20)

	for k, v := range colWidths {
		xlsx.SetColWidth("Sheet1", k, k, v)
	}

	for i, v := range strings.Split(fmt.Sprintf("%v", selectedItems), "|") {
		selectedItemMap[v] = i
	}

	p := map[string]struct{}{
		"residual_points": {},
		"recharge_points": {},
		"bonus_points":    {},
		"refund_points":   {},
		"total_points":    {},
	}

	v := req.(map[string]interface{})
	for index, vv := range v["list"].([]interface{}) {

		var (
			columIndex  = 'A'
			columOffset = 1
		)

		for kkk, vvv := range pkgUtil.Struct2Map(vv, "json") {

			if len(selectedItemMap) > 0 {
				if i, ok := selectedItemMap[kkk]; !ok {
					continue
				} else {
					columOffset = i
				}
			}

			cell := fmt.Sprintf("%c", int(columIndex)+columOffset) + convert.ToString(index+2)

			if reflect.TypeOf(vvv).Kind() == reflect.Ptr {
				vOf := reflect.ValueOf(vvv)
				if vOf.IsNil() {
					vvv = ""
				} else {
					vvv = vOf.Elem()
				}
			}

			if kkk == "create_at" {
				vvv = pkgTime.Format(vvv.(int64))
			}

			if kkk == "id" {
				vvv = seqs[fmt.Sprintf("%v", vvv.(int64))]
			}

			if _, ok := p[kkk]; ok {
				vvv = pkgUtil.Decimal(float64(vvv.(uint64))/10000.0, 5)
			}

			xlsx.SetCellValue("Sheet1", cell, vvv)
		}

		xlsx.SetRowHeight("Sheet1", index+2, 18)
	}

	xlsx.SetSheetName("Sheet1", "第一页")

	fileName, filePath := a.getExportPath(convert.ToString(time.Now().UnixMilli())+".csv", funcTy)
	err := xlsx.SaveAs(fileName)
	if err != nil {
		return nil
	}

	req = filePath

	return req
}

func (a *RechargeSrv) getExportPath(fileName string, bizTy int) (string, string) {

	var (
		bizTyMap = map[int]string{
			1: "recharge",
		}
	)

	relativePath := fmt.Sprintf("%v/export/%v", bizTyMap[bizTy], fileName)

	return serviceUtil.GetFilePath(
		fmt.Sprintf("%v/%v",
			serviceUtil.FileStorage(),
			relativePath),
	), relativePath
}
